// SelectionFilters.h
//
#ifndef SELECTION_FILTERS_H
#define SELECTION_FILTERS_H

#include "SelectionSet.h"
#include "GraphicScene.h"

namespace IDS{
namespace IDEAS{
namespace OpenGLView{

//---------------------------------------------------------------------------------------------
// Selection filter class:
// - VObjectType must be VObjectGL or a type derived from it.
// - qty is passed to the constructor of SelectionRequest.
// The method 'apply' applies the filter to the selection set and excludes all the objects with the wrong type.
// The selected objects are saved:
// - if qty==1, in the field 'object'
// - if qty==2, in the fields 'firstObject' and 'secondObject'
// - otherwise, in the vector 'objects'
// The method 'isRequestFulfilled' returns true if the selection set contains the requested number of items.
template <class VObjectType, int qty> class SelectionFilter;

//---------------------------------------------------------------------------------------------
// Base selection filter class
class SelectionFilterBase
{
public:
	SelectionFilterBase() : fulfilled(false) { }
	bool isRequestFulfilled() const { return this->fulfilled; }

	// Apply the filter to the selection set and process the object list
	void apply(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		selectionSet.setDefaultRequest();
		this->addRequests(selectionSet);
		selectionSet.refresh();
		this->fulfilled = this->getObjects(selectionSet);
	}

private:
	bool fulfilled;
	virtual void addRequests(GraphicScene::IDEASSelectionSet& selectionSet) = 0;
	virtual bool getObjects(GraphicScene::IDEASSelectionSet& selectionSet) = 0;
};

// Conversion from VObjectType to GeometricType enumerator value
template <class VObjectType> struct GeometricTypeFor { };
template <> struct GeometricTypeFor<VObjectGL> { static const VObjectGL::GeometricType id = VObjectGL::Any; };
template <> struct GeometricTypeFor<VPointGL> { static const VObjectGL::GeometricType id = VObjectGL::Point; };
template <> struct GeometricTypeFor<VCurveGL> { static const VObjectGL::GeometricType id = VObjectGL::Curve; };
template <> struct GeometricTypeFor<VShapeGL> { static const VObjectGL::GeometricType id = VObjectGL::Shape; };

// Selection filter for a variable amount of items (used for almost all values of qty)
template <class VObjectType>
class SelectionFilterMany : public SelectionFilterBase
{
public:
	SelectionFilterMany(int p_count) : count(p_count) { }
	int count;
	std::vector< std::tr1::shared_ptr<VObjectType> > objects;

private:
	virtual void addRequests(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		selectionSet.addRequest( SelectionRequest<VObjectGL::GeometricType>(GeometricTypeFor<VObjectType>::id, this->count) );
	}
	virtual bool getObjects(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		this->objects.clear();
		GraphicScene::IDEASSelectionSet::SelectionList& objectList = selectionSet.getObjects();
		GraphicScene::IDEASSelectionSet::SelectionList::iterator it;
		for (it = objectList.begin(); it != objectList.end(); it++)
		{
			std::tr1::shared_ptr<VObjectType> obj = std::dynamic_pointer_cast<VObjectType>(it->second);
			if (obj != NULL)
				this->objects.push_back(obj);
		}
		return (int)this->objects.size() >= (count < 0 ? 1 : count);
	}
};

template <class VObjectType, int qty>
class SelectionFilter : public SelectionFilterMany<VObjectType>
{
	SelectionFilter() : SelectionFilterMany(qty) { }
};

// Template specialization for one item
template <class VObjectType>
class SelectionFilter<VObjectType,1> : public SelectionFilterBase
{
public:
	SelectionFilter() : object(NULL) { }
	std::tr1::shared_ptr<VObjectType> object;

private:
	virtual void addRequests(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		selectionSet.addRequest( SelectionRequest<VObjectGL::GeometricType>(GeometricTypeFor<VObjectType>::id, 1) );
	}
	virtual bool getObjects(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		this->object = NULL;
		GraphicScene::IDEASSelectionSet::SelectionList& objectList = selectionSet.getObjects();
		if (!objectList.empty())
			this->object = std::dynamic_pointer_cast<VObjectType>(objectList.begin()->second);
		return this->object != NULL;
	}
};

// Selection filter for two items
template <class VObjectType>
class SelectionFilter<VObjectType,2> : public SelectionFilterBase
{
public:
	SelectionFilter() : firstObject(NULL), secondObject(NULL) { }
	std::tr1::shared_ptr<VObjectType> firstObject;
	std::tr1::shared_ptr<VObjectType> secondObject;

private:
	virtual void addRequests(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		selectionSet.addRequest( SelectionRequest<VObjectGL::GeometricType>(GeometricTypeFor<VObjectType>::id, 2) );
	}
	virtual bool getObjects(GraphicScene::IDEASSelectionSet& selectionSet)
	{
		this->firstObject = this->secondObject = NULL;
		GraphicScene::IDEASSelectionSet::SelectionList& objectList = selectionSet.getObjects();
		GraphicScene::IDEASSelectionSet::SelectionList::iterator it = objectList.begin();
		if (it != objectList.end())
		{
			this->firstObject = std::dynamic_pointer_cast<VObjectType>(it->second);
			if (++it != objectList.end())
				this->secondObject = std::dynamic_pointer_cast<VObjectType>(it->second);
		}
		return (this->firstObject != NULL) && (this->secondObject != NULL);
	}
};

}}}

#endif
